package com.mask.token.security;

import com.mask.token.config.MaskTokenProperties;
import com.mask.token.security.handler.MaskAuthenticationEntryPoint;
import com.mask.token.security.handler.MaskAccessDeniedHandler;
import com.mask.token.security.filter.MaskTokenAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

/**
 * Mask Token 安全配置
 *
 * @author mask
 * @since 1.0.0
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class MaskTokenSecurityConfig extends WebSecurityConfigurerAdapter {

    private final MaskTokenProperties properties;
    private final MaskTokenAuthenticationFilter tokenAuthenticationFilter;
    private final MaskAuthenticationEntryPoint authenticationEntryPoint;
    private final MaskAccessDeniedHandler accessDeniedHandler;

    @Bean
    @ConditionalOnMissingBean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用CSRF
        if (!properties.isEnableCsrf()) {
            http.csrf().disable();
        }
        
        // 配置CORS
        if (properties.isEnableCors()) {
            http.cors().configurationSource(maskTokenCorsConfigurationSource());
        } else {
            http.cors().disable();
        }
        
        // 配置Session管理
        if (properties.isEnableSessionManagement()) {
            http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .maximumSessions(properties.getMaxSessions())
                .maxSessionsPreventsLogin(properties.isPreventLogin());
        } else {
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
        
        // 配置授权规则
        http.authorizeRequests()
            // 忽略认证的URL
            .antMatchers(properties.getIgnoreUrls().toArray(new String[0])).permitAll()
            .antMatchers(properties.getIgnoreStaticResources().toArray(new String[0])).permitAll()
            // 其他请求需要认证
            .anyRequest().authenticated();
        
        // 配置异常处理
        http.exceptionHandling()
            .authenticationEntryPoint(authenticationEntryPoint)
            .accessDeniedHandler(accessDeniedHandler);
        
        // 添加Token认证过滤器
        http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean("maskTokenCorsConfigurationSource")
    @ConditionalOnMissingBean(name = "corsConfigurationSource")
    public CorsConfigurationSource maskTokenCorsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 使用兼容的方法设置允许的源
        configuration.setAllowedOrigins(properties.getAllowedOrigins());
        configuration.setAllowedMethods(properties.getAllowedMethods());
        configuration.setAllowedHeaders(properties.getAllowedHeaders());
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
